package GUI;

import game.Character;
import game.GameBase;
import game.ItemContainer;
import game.Portal;
import game.Tile;
import game.TileMap;
import game.pathfinding.AStar;
import game.pathfinding.Path;
import game.pathfinding.PathElement;
import game.pathfinding.Pathfinder;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class MapView extends JPanel implements MouseListener, ComponentListener, ActionListener
{
	private static final long serialVersionUID = -8353113464959478182L;
	private static final int CHAR_ANIM_STEPS = 8;
	private static final int CHAR_ANIM_PIX = Tile.TILE_SIZE / CHAR_ANIM_STEPS;
	
	private TileMap map = null;
	private int offsetX = 0,offsetY = 0;
	private HUD hud = null;
	private game.Character toFollow = null;
	private Path path = null;
	private boolean isMoving = false;
	
	private ContainerTransactionPanel transPanel = null;
	private InventoryPanel invPanel = null;
	
	private Thread t;
	
	public Path getPath() {
		return path;
	}

	public void setPath(Path path) {
		this.path = path;
		repaint();
	}

	private BufferedImage pathDot1 = null;
	private BufferedImage pathDot2 = null;
	
	private SkinnedButton invButton = null;
	private SkinnedButton turnButton = null;
	
	public HUD getHud() {
		return hud;
	}

	public void setHud(HUD hud) {
		this.hud = hud;
	}

	private ArrayList<ActionListener> clickListeners = new ArrayList<ActionListener>();
	
	public MapView()
	{
		super();
		this.addMouseListener(this);
		this.addComponentListener(this);
		this.setLayout(null);
		
		pathDot1 = GameBase.getImageByTag("path-dot");
		pathDot2 = GameBase.getImageByTag("path-dot2");
		
		transPanel = new ContainerTransactionPanel();
		transPanel.setVisible(false);
		add(transPanel);
		transPanel.addComponentListener(this);
		transPanel.setLocation(50, 50);
		transPanel.setSize(transPanel.getPreferredSize());
		
		invPanel = new InventoryPanel();
		invPanel.setVisible(false);
		add(invPanel);
		invPanel.addComponentListener(this);
		invPanel.setSize(invPanel.getPreferredSize());
		
		invButton = new SkinnedButton(ButtonSkin.getButtonByTag("Button1"), "Inventory");
		turnButton = new SkinnedButton(ButtonSkin.getButtonByTag("Button1"), "Pass turn");
		
		add(invButton);
		add(turnButton);
		
		invButton.setSize(100, 32);
		turnButton.setSize(100, 32);
		
		invButton.setLocation(200, this.getHeight() - 40);
		turnButton.setLocation(this.getWidth() - 300, this.getHeight() - 40);
		
		invButton.addActionListener(this);
		turnButton.addActionListener(this);
		
		t = new Thread(new Runnable() {
		
			@Override
			public void run() 
			{
				PathElement elem1 = null, elem2 = null;
				Character pc = null;
				int step = 0, dx = 0, dy = 0;
				
				while(true) 
				{
					if(path != null && isMoving)
					{
						if(elem1 == null && elem2 == null && !path.stops.isEmpty())
						{
							pc = Game.get().getPlayer();
							
							elem1 = new PathElement(pc.getPosX(), pc.getPosY(), true);
							elem2 = path.stops.firstElement();
							if(!elem2.fits) 
							{ 
								pc.getAnimator().stop(); 
								isMoving = false; 
								elem1 = elem2 = null;
								System.out.println("Movement pts spent");
								continue; 
							}
							
							pc.pixX = 0;
							pc.pixY = 0;
							pc.getAnimator().play();
							step = 0;
							
							dx = elem2.X - elem1.X;
							dy = elem2.Y - elem1.Y;
							pc.assignAnimation(dx, dy);
							
							if(dx == 1 || dy == 1)
							{
								pc.setPosX(elem2.X);
								pc.setPosY(elem2.Y);
								pc.pixX = -dx * Tile.TILE_SIZE;
								pc.pixY = -dy * Tile.TILE_SIZE;
							}
						}
						
						if(step == CHAR_ANIM_STEPS)
						{
							pc.Move(elem2.X, elem2.Y);
							repaintLowerHUD();
							boundsCheck();
							path.stops.remove(0);
							elem1 = elem2;
							pc.pixX = 0; pc.pixY = 0;
							step = 0;
							
							if(path.stops.isEmpty())
							{
								path = null;
								isMoving = false;
								elem1 = null;
								elem2 = null;
								pc.getAnimator().stop();
								repaintChars();
								repaintLowerHUD();
								handleStop();
								continue;
							}
							else
							{
								elem2 = path.stops.firstElement();
								
								if(!elem2.fits) { isMoving = false; elem1 = elem2 = null; repaintChars(); continue; }
								
								dx = elem2.X - elem1.X;
								dy = elem2.Y - elem1.Y;
								pc.assignAnimation(dx, dy);
								
								
								if(dx == 1 || dy == 1)
								{
									pc.setPosX(elem2.X);
									pc.setPosY(elem2.Y);
									pc.pixX = -dx * Tile.TILE_SIZE;
									pc.pixY = -dy * Tile.TILE_SIZE;
								}
							}
						}
						
						step++;
						pc.pixX += dx * CHAR_ANIM_PIX;
						pc.pixY += dy * CHAR_ANIM_PIX;
						repaintChars();
					}
					try
					{
						Thread.sleep(100);
					} catch (InterruptedException e) {}
				}
				
			}
		}
		);
		
		t.start();
	}
	
	private void handleStop()
	{
		int x = Game.get().getPlayer().getPosX();
		int y = Game.get().getPlayer().getPosY();
		
		if(map.getItemAt(x, y) != null)
		{
			ItemContainer mapC = map.getContainerAt(x, y);
			
			transPanel.setLeftContainer(Game.get().getPlayer().getInventory(), "Your inventory");
			transPanel.setRightContainer(mapC, "Chest");
			
			Rectangle rect = new Rectangle(0, 0, this.getWidth(), this.getHeight());
			transPanel.setLocation(rect.x + (rect.width - transPanel.getWidth()) / 2,
					rect.y + (rect.height - transPanel.getHeight()) / 2);
			transPanel.setVisible(true);
		}
		else if(map.getPortalAt(x, y) != null)
		{
			Portal port = map.getPortalAt(x, y);
			TileMap m = port.getDestMap();
			if(m != null)
			{
				this.setMap(m);
				Game.get().getPlayer().assignAnimation(0, 1);
				Game.get().getPlayer().setMap(m);
				Game.get().setMap(m);
				m.addCharacter(Game.get().getPlayer(), port.getDstX(), port.getDstY());
				centerOn(Game.get().getPlayer());
				path = null;
				isMoving = false;
				System.out.println("Blink!");
			}
			else
			{
				System.out.println("No dest map");
			}
		}
	}

	public TileMap getMap() {
		return map;
	}

	public void setMap(TileMap map) 
	{
		this.map = map;
		offsetX = 0;
		offsetY = 0;
		repaint();
	}

	public void paintComponent(Graphics g1)
	{
		Graphics2D g = (Graphics2D) g1;
		
		int w = getWidth() / Tile.TILE_SIZE + 1;
		int h = getHeight() / Tile.TILE_SIZE + 1;
		
		g.setColor(Color.black);
		g.fillRect(0, 0, getWidth(), getHeight());
		
		if(map == null) return;
		BufferedImage chest = GameBase.getImageByTag("kovceg");
		
		for(int y = offsetY;y < offsetY + h;y++)
		{
			for(int x = offsetX;x < offsetX + w;x++)
			{
				if(map.getTileAt(x, y) == null) continue;
				BufferedImage image = map.getTileAt(x, y).getImg();
				
				if(image != null) 
				{
				g.drawImage(image, (x-offsetX)*Tile.TILE_SIZE, (y-offsetY)*Tile.TILE_SIZE - map.getTileAt(x, y).getVOffset(), null);
				}
				
				if(map.getItemAt(x, y) != null)
				{
					g.drawImage(chest, (x-offsetX)*Tile.TILE_SIZE, (y-offsetY)*Tile.TILE_SIZE, null);
				}
				//if()
			
				game.Character c = map.getCharacterAt(x, y);
				if(c != null)
				{
					c.getAnimator().drawFrame(g, (x - offsetX) * Tile.TILE_SIZE + c.pixX + Tile.TILE_SIZE / 2,
							(y-offsetY)*Tile.TILE_SIZE + c.pixY + Tile.TILE_SIZE / 2);
					g.setColor(Color.white);
					g.drawString(c.getName(), (x-offsetX)*Tile.TILE_SIZE + c.pixX, (y-offsetY)*Tile.TILE_SIZE + c.pixY);
				}
				
			}
			/*
			for(int x = offsetX;x < offsetX + w;x++)
			{
				game.Character c = map.getCharacterAt(x, y);
				if(c != null)
				{
					c.getAnimator().drawFrame(g, (x - offsetX) * Tile.TILE_SIZE + c.pixX + Tile.TILE_SIZE / 2,
							(y-offsetY)*Tile.TILE_SIZE + c.pixY + Tile.TILE_SIZE / 2);
					g.setColor(Color.white);
					g.drawString(c.getName(), (x-offsetX)*Tile.TILE_SIZE + c.pixX, (y-offsetY)*Tile.TILE_SIZE + c.pixY);
				}
			}
				*/
		}
		
		if(path != null)
		{
			int n = 0;
			g.setColor(Color.red);
			for(PathElement pe : path.stops)
			{
				if(pe.fits)
					g.drawImage(pathDot1, (pe.X-offsetX)*Tile.TILE_SIZE, (pe.Y-offsetY)*Tile.TILE_SIZE, null);
				else
					g.drawImage(pathDot2, (pe.X-offsetX)*Tile.TILE_SIZE, (pe.Y-offsetY)*Tile.TILE_SIZE, null);
				//g.drawString("(" + n + ")", (pe.X-offsetX)*Tile.TILE_SIZE, (pe.Y-offsetY)*Tile.TILE_SIZE + 20);
				n++;
			}
		}
		
		if(hud != null) hud.draw(g, this.getWidth(), this.getHeight());
	}
	
	public int getOffsetX() {
		return offsetX;
	}

	public void setOffsetX(int offsetX) {
		this.offsetX = offsetX;
	}

	public int getOffsetY() {
		return offsetY;
	}

	public void setOffsetY(int offsetY) {
		this.offsetY = offsetY;
	}
	
	public void centerOn(int x, int y)
	{
		int w = (getWidth() / Tile.TILE_SIZE) / 2;
		int h = (getHeight() / Tile.TILE_SIZE) / 2;
		
		offsetX = x - w;
		offsetY = y - h;
		
		repaint();
	}
	
	public void centerOn(game.Character c)
	{
		centerOn(c.getPosX(), c.getPosY());
		toFollow = c;
	}

	public void mouseClicked(MouseEvent arg0) 
	{
		
	}
	
	private void boundsCheck()
	{
		if(toFollow == null) return;
		
		int w = ((getWidth() / 3) / Tile.TILE_SIZE);
		int h = ((getHeight() / 3) / Tile.TILE_SIZE);
		
		if(toFollow.getPosX() - offsetX < w) centerOn(toFollow);
		if(toFollow.getPosX() - offsetX > w * 2) centerOn(toFollow);
		if(toFollow.getPosY() - offsetY < h) centerOn(toFollow);
		if(toFollow.getPosY() - offsetY > h * 2) centerOn(toFollow);
	}
	
	private void repaintChars()
	{
		if(map == null) return;
		
		int w = getWidth() / Tile.TILE_SIZE + 1;
		int h = getHeight() / Tile.TILE_SIZE + 1;
		
		for(int y = offsetY;y < offsetY + h;y++)
		{
			for(int x = offsetX;x < offsetX + w;x++)
			{
				game.Character c = map.getCharacterAt(x, y);
				if(c != null)
				{
					int sX = (x-offsetX)*Tile.TILE_SIZE + Tile.TILE_SIZE / 2 + c.pixX, 
					sY = (y-offsetY)*Tile.TILE_SIZE +  + Tile.TILE_SIZE / 2 + c.pixY;
					repaint(sX - Tile.TILE_SIZE, sY - Tile.TILE_SIZE,
							Tile.TILE_SIZE * 2, Tile.TILE_SIZE * 2);
				}
			}
		}
	}

	@Override
	public void mouseEntered(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mouseExited(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mousePressed(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mouseReleased(MouseEvent arg0) {
		int x = arg0.getX();
		int y = arg0.getY();
		
		Point source = new Point((x/Tile.TILE_SIZE)+offsetX,(y/Tile.TILE_SIZE)+offsetY);
		System.out.println(source);
		NotifyListeners(source, "mouseClick");
		
		//////  PLACEHOLDER  //////
		if(transPanel.isVisible())
			transPanel.setVisible(false);
		if(invPanel.isVisible())
			invPanel.setVisible(false);
		
		if(arg0.getButton() == MouseEvent.BUTTON2)
		{
			if(map.inMap(source.x, source.y))
			{
				if(map.getCharacterAt(source.x, source.y) != null)
				{
					new InfoWindow(map.getCharacterAt(source.x, source.y).toString(), (Window) this.getTopLevelAncestor());
				}
				
				if(map.getItemAt(source.x, source.y) != null)
				{
					//InfoWindow info = new InfoWindow(map.getItemAt(source.x, source.y).getHTML(), true);
					//info.setLocation(x + this.getLocationOnScreen().x, y + this.getLocationOnScreen().y);
					InfoWindow.setTooltipBounds(new Rectangle(this.getLocationOnScreen(), this.getSize()));
					InfoWindow.showToolTip(map.getItemAt(source.x, source.y).getHTML(),
							x + this.getLocationOnScreen().x - 5, y + this.getLocationOnScreen().y - 5,
							(Window) this.getTopLevelAncestor());
				}
			}
		}
		else if(arg0.getButton() == MouseEvent.BUTTON1)
		{
			if(map.inMap(source.x, source.y) && map.getCharacterAt(source.x, source.y) != null)
			{
				centerOn(map.getCharacterAt(source.x, source.y));
			}
			else
			{
				toFollow = null;
				centerOn(source.x, source.y);
			}
		}
		else
		{
			if(map.inMap(source.x, source.y) && !isMoving)
			{
				if(path == null || path.getElementAt(source.x, source.y) == null)
				{
					Pathfinder pf = new AStar();
					Path p = pf.findPath(map, 
							Game.get().getPlayer().getPosX(), Game.get().getPlayer().getPosY(),
							source.x, source.y, Game.get().getPlayer().getMovementPointsLeft());
					setPath(p);
				}
				else
				{
					/*PathElement elem = path.getElementAt(source.x, source.y);
					if(elem.fits)
					{
						path.advanceTo(elem);
						Game.get().getPlayer().Move(elem.X, elem.Y);
					}
					else
					{
						path.advanceTo(path.lastFit);
						Game.get().getPlayer().Move(path.lastFit.X, path.lastFit.Y);
					}
					repaint();*/
					isMoving = true;
				}
			}
			else
			{
				System.out.println("Out of map or moving (" + isMoving + "!");
			}
		}
		//////  PLACEHOLDER  //////
	}
	
	public void AddListener(ActionListener listener)
	{
		clickListeners.add(listener);
	}
	
	public void RemoveListener(ActionListener listener)
	{
		clickListeners.remove(listener);
	}
	
	private void NotifyListeners(Object source,String action) 
	{
		ActionEvent event = new ActionEvent(source, 0, action);
		
		for(int i = 0;i < clickListeners.size();i++)
		{
			clickListeners.get(i).actionPerformed(event);
		}
	}
	
	private void repaintLowerHUD()
	{
		repaint(0, this.getHeight() - 100, this.getWidth(), 100);
	}

	@Override
	public void componentHidden(ComponentEvent arg0) 
	{
		System.out.println("Component hidden!");
		if(arg0.getSource() == transPanel)
		{
			repaint();
		}
	}

	@Override
	public void componentMoved(ComponentEvent arg0) {}

	@Override
	public void componentResized(ComponentEvent arg0) {
		if(arg0.getSource() == this)
		{
			boundsCheck();
			invButton.setLocation(200, this.getHeight() - 40);
			turnButton.setLocation(this.getWidth() - 300, this.getHeight() - 40);
		}
	}

	@Override
	public void componentShown(ComponentEvent arg0) {}

	@Override
	public void actionPerformed(ActionEvent e) {
		if(e.getSource() == turnButton)
		{
			if(!isMoving)
			{
				Game.get().passTurn();
				if(path != null)
				{
					int x = path.stops.lastElement().X;
					int y = path.stops.lastElement().Y;
					Pathfinder pf = new AStar();
					Path p = pf.findPath(Game.get().getMap(), 
							Game.get().getPlayer().getPosX(), Game.get().getPlayer().getPosY(),
							x, y, Game.get().getPlayer().getMovementPointsLeft());
					setPath(p);
				}
				repaint();
			}
		}
		else if(e.getSource() == invButton)
		{
			if(invPanel != null)
			{
				Rectangle rect = new Rectangle(0, 0, this.getWidth(), this.getHeight());
				invPanel.setLocation(0, rect.y + (rect.height - transPanel.getHeight()) / 2);
				invPanel.setVisible(!invPanel.isVisible());
			}
		}
	}
	
	
}
